package com.simple.quotes.shockdays;

import com.simple.quotes.core.Quote;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Анализ котировок на наличие ударных дней (УД)
 * @author Petro
 * @deprecated
 */
public class ShockDays {
    private static final Logger log = Logger.getLogger(ShockDays.class);
    //Ударные дни
    private static List<Calendar> shockDays = new ArrayList<Calendar>();

    /**
     * Определение "ударных" дней (shock days) в периоде
     * @param qouteList список всех тиков
     * @param delta дельта
     */
    public static void calculate(List<Quote> qouteList, Double delta) {
        log.info("Calculating shock days in period ...");
        //разница между ценой открытия и ценой закрытия в процентах
        if(delta == null){
            log.warn("Delta not specified. Default value (0.02) will be used.");
            delta = 0.2;
        }

        //минимальная точка в диапазоне с 10:00 до 11:30, после которой будет только рост
        Double riseEtalonValue = Double.MAX_VALUE;
        //максимальная точка в диапазоне с 10:00 до 11:30, после которой будет только падение
        Double fallEtalonValue = Double.MIN_VALUE;
        int oldDay = 0; //день месяца
        int newDay = 0; //день месяца
        Calendar day1000 = Calendar.getInstance(); //10:00:00 - 1 секунда каждого дня
        Calendar day1130 = Calendar.getInstance(); //11:30:00 + 1 секунда каждого дня
        boolean isRiseShockDay = false; //признак ударного дня в рост
        boolean isFallShockDay = false; //признак ударного дня в падение
        Double value1130 = 0.0; //цена в 11:30
        Double valueClose = 0.0; //цена на закрытии дня

        for(Quote qoute : qouteList)
        {
            newDay = qoute.getDateTime().get(Calendar.DAY_OF_MONTH);
            if(oldDay != newDay)
            { //Начинаем новый день: обнуляем переменные
                //если предыдущий день оказался ударным, то добавляем его в список
                if((isRiseShockDay || isFallShockDay) && Math.abs(valueClose - value1130)/value1130 >= delta)
                    shockDays.add(day1000);
                //обнуляем старое цену закрытия
                valueClose = 0.0;
                //обнуляем эталоны
                riseEtalonValue = Double.MAX_VALUE;
                fallEtalonValue = Double.MIN_VALUE;
                //обновляем 10:00:00 и 11:30:00
                day1000 = (Calendar)qoute.getDateTime().clone();
                day1000.set(Calendar.HOUR_OF_DAY, 9);
                day1000.set(Calendar.MINUTE, 59);
                day1000.set(Calendar.SECOND, 59);
                day1130 = (Calendar)qoute.getDateTime().clone();
                day1130.set(Calendar.HOUR_OF_DAY, 11);
                day1130.set(Calendar.MINUTE, 30);
                day1130.set(Calendar.SECOND, 1);
                //устанавливаем признак ударного дня. если какому-то условию
                //не удовлетворит, то признак снимается
                isRiseShockDay = true;
                isFallShockDay = true;
                oldDay = newDay;
            }
            else
            {
                //запоминаем цену в 11:30
                if(qoute.getDateTime().get(Calendar.HOUR) == 11
                    && qoute.getDateTime().get(Calendar.MINUTE) == 30
                    && qoute.getDateTime().get(Calendar.SECOND) == 0)
                    value1130 = qoute.getOpen();
                if (!isRiseShockDay && !isFallShockDay)
                    continue;
            }

            if(qoute.getDateTime().after(day1000) && qoute.getDateTime().before(day1130))
            { //с 10:00:00 до 11:30:00
                riseEtalonValue = (qoute.getClose() < riseEtalonValue ? qoute.getClose() : riseEtalonValue);
                fallEtalonValue = (qoute.getClose() > fallEtalonValue ? qoute.getClose() : fallEtalonValue);
            }
            else
            { //с 11:30:00
                if(qoute.getClose() <= riseEtalonValue)
                    isRiseShockDay = false;
                if(qoute.getClose() >= fallEtalonValue)
                    isFallShockDay = false;
            }
            //запоминаем каждое закрытие. когда начнется новый день - это и будет реальное значение закрытия дня
            valueClose = qoute.getClose();
        }
        log.info("completed successfully. Shock days count = " + shockDays.size());
    }

    public static List<Calendar> getList(){
        return shockDays;
    }

    public static void print(){
        if (!shockDays.isEmpty()) {
            log.info("Shock days:");
            int i = 1;
            for (Calendar cal : shockDays) {
                log.info(String.format("%d. %2$tY.%2$tm.%2$td%n", i++, cal));
            }
        }
    }

    /**
     * Получение статистики УД по месяцам
     */
    public static void getMonthlyStatistics()
    {
        if(shockDays.isEmpty())
        {
            log.warn("Shock days list is empty.");
            return;
        }

        //<Месяц, Кол-во УД в месяце>
        Map<Integer, Integer> mapMonthCountShockDays = new HashMap<Integer, Integer>();
        //<Месяц, Кол-во месяцев>
        Map<Integer, Integer> mapMonthCountMonths = new HashMap<Integer, Integer>();
        
        int oldMonth = 0;
        int newMonth = 0;
        for(Calendar day : shockDays)
        {
            Integer countShockDays = mapMonthCountShockDays.get(day.get(Calendar.MONTH));
            if(countShockDays == null)
                countShockDays = 0;
            mapMonthCountShockDays.put(day.get(Calendar.MONTH), ++countShockDays);

            newMonth = day.get(Calendar.MONTH);
            if(newMonth != oldMonth)
            {
                Integer countMonths = mapMonthCountMonths.get(day.get(Calendar.MONTH));
                if(countMonths == null)
                    countMonths = 0;
                mapMonthCountMonths.put(day.get(Calendar.MONTH), ++countMonths);
            }
            oldMonth = newMonth;
        }

        log.info("Shock days statistics:");
        for(Integer month : mapMonthCountShockDays.keySet())
        {
            Integer countShockDays = mapMonthCountShockDays.get(month);
            if(countShockDays == null)
                countShockDays = 0;

            Integer countMonths = mapMonthCountMonths.get(month);
            if(countMonths == null)
                countMonths = 1;

            Double meanCountShockDays = new Double(countShockDays) / countMonths;
            System.out.println(CalendarHelper.getMonthName(month) + " - " + meanCountShockDays);
        }
    }
}
